﻿using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.TagHelpers;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;

namespace XW.Html.TagHelpers
{
    /// <summary>
    /// 
    /// </summary>
    /// <seealso cref="Microsoft.AspNetCore.Razor.TagHelpers.TagHelper" />
    [HtmlTargetElement("ly-input", Attributes = "asp-for")]
    public class LyInputTagHelper : TagHelper
    {
        public LyInputTagHelper(IHtmlGenerator generator)
        {
            Generator = generator;
        }

        private static readonly Dictionary<string, string> _defaultInputTypes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
    {
        {
            "HiddenInput",
            InputType.Hidden.ToString().ToLowerInvariant()
        },
        {
            "Password",
            InputType.Password.ToString().ToLowerInvariant()
        },
        {
            "Text",
            InputType.Text.ToString().ToLowerInvariant()
        },
        { "PhoneNumber", "tel" },
        { "Url", "url" },
        { "EmailAddress", "email" },
        { "Date", "date" },
        { "DateTime", "datetime-local" },
        { "DateTime-local", "datetime-local" },
        { "DateTimeOffset", "text" },
        { "Time", "time" },
        { "Week", "week" },
        { "Month", "month" },
        { "Byte", "number" },
        { "SByte", "number" },
        { "Int16", "number" },
        { "UInt16", "number" },
        { "Int32", "number" },
        { "UInt32", "number" },
        { "Int64", "number" },
        { "UInt64", "number" },
        {
            "Single",
            InputType.Text.ToString().ToLowerInvariant()
        },
        {
            "Double",
            InputType.Text.ToString().ToLowerInvariant()
        },
        {
            "Boolean",
            InputType.CheckBox.ToString().ToLowerInvariant()
        },
        {
            "Decimal",
            InputType.Text.ToString().ToLowerInvariant()
        },
        {
            "String",
            InputType.Text.ToString().ToLowerInvariant()
        },
        { "IFormFile", "file" },
        { "IEnumerable`IFormFile", "file" }
    };
        private const string ForAttributeName = "asp-for";

        /// <summary>
        /// 类型
        /// </summary> 
        [HtmlAttributeName("type")]
        public string InputTypeName { get; set; }

        [HtmlAttributeName("asp-format")]
        public string Format { get; set; }

        [HtmlAttributeName(ForAttributeName)]
        public ModelExpression For { get; set; }
        public override int Order => -1000;

 
        [HtmlAttributeName("form")]
        public string FormName { get; set; }
        [HtmlAttributeName("name")]
        public string Name { get; set; }
        [HtmlAttributeName("value")]
        public string Value { get; set; }


        protected IHtmlGenerator Generator { get; }

        [HtmlAttributeNotBound]
        [ViewContext]
        public ViewContext ViewContext { get; set; }

        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            TagHelperAttributeList attributeList = output.Attributes;
            output.TagName = "input";
            output.TagMode = TagMode.StartTagAndEndTag;
            if (InputTypeName != null)
            {
                output.CopyHtmlAttribute("type", context);
            }
            if (Name != null)
            {
                output.CopyHtmlAttribute("Name", context);
            }
            if (Value != null)
            {
                output.CopyHtmlAttribute("Value", context);
            }
            if (FormName != null)
            {
                output.CopyHtmlAttribute("form", context);
            }

            ModelMetadata metadata = For.Metadata;
            ModelExplorer modelExplorer = For.ModelExplorer;
            string text;
            string inputTypeHint;
            if (string.IsNullOrEmpty(InputTypeName))
            {
                text = GetInputType(modelExplorer, out inputTypeHint);
            }
            else
            {
                text = InputTypeName.ToLowerInvariant();
                inputTypeHint = null;
            }
            if (!output.Attributes.ContainsName("type"))
            {
                output.Attributes.SetAttribute("type", text);
            }
            IDictionary<string, object> htmlAttributes = null;
            if (string.IsNullOrEmpty(For.Name) && string.IsNullOrEmpty(ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix) && !string.IsNullOrEmpty(Name))
            {
                htmlAttributes = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase) { { "name", Name } };
            } 
            CultureInfo formatProvider = CultureInfo.InvariantCulture;
            //formatProvider = CultureInfo.CurrentCulture;
            string text2 = FormatValue(modelExplorer.Model, Format, formatProvider);

            if (!string.IsNullOrEmpty(text2))
            {
                output.Attributes.SetAttribute("value", text2);
            }
            base.Process(context, output);
        }
        protected string GetInputType(ModelExplorer modelExplorer, out string inputTypeHint)
        {
            foreach (string inputTypeHint2 in GetInputTypeHints(modelExplorer))
            {
                if (_defaultInputTypes.TryGetValue(inputTypeHint2, out var value))
                {
                    inputTypeHint = inputTypeHint2;
                    return value;
                }
            }
            inputTypeHint = InputType.Text.ToString().ToLowerInvariant();
            return inputTypeHint;
        }


        private static IEnumerable<string> GetInputTypeHints(ModelExplorer modelExplorer)
        {
            if (!string.IsNullOrEmpty(modelExplorer.Metadata.TemplateHint))
            {
                yield return modelExplorer.Metadata.TemplateHint;
            }
            if (!string.IsNullOrEmpty(modelExplorer.Metadata.DataTypeName))
            {
                yield return modelExplorer.Metadata.DataTypeName;
            }
            Type type = modelExplorer.ModelType;
            if (typeof(bool?) != type)
            {
                type = modelExplorer.Metadata.UnderlyingOrModelType;
            }
            foreach (string typeName in  GetTypeNames(modelExplorer.Metadata, type))
            {
                yield return typeName;
            }
        }

        public static IEnumerable<string> GetTypeNames(ModelMetadata modelMetadata, Type fieldType)
        {
            if (typeof(IEnumerable<IFormFile>) != fieldType)
            {
                yield return fieldType.Name;
            }
            if (fieldType == typeof(string))
            {
                yield break;
            }
            if (!modelMetadata.IsComplexType)
            {
                if (fieldType.IsEnum)
                {
                    yield return "Enum";
                }
                else if (fieldType == typeof(DateTimeOffset))
                {
                    yield return "DateTime";
                }
                yield return "String";
                yield break;
            }
            if (!fieldType.IsInterface)
            {
                Type type = fieldType;
                while (true)
                {
                    type = type.BaseType;
                    if (type == null || type == typeof(object))
                    {
                        break;
                    }
                    yield return type.Name;
                }
            }
            if (typeof(IEnumerable).IsAssignableFrom(fieldType))
            {
                if (typeof(IEnumerable<IFormFile>).IsAssignableFrom(fieldType))
                {
                    yield return "IEnumerable`IFormFile";
                    if (typeof(IEnumerable<IFormFile>) == fieldType)
                    {
                        yield return fieldType.Name;
                    }
                }
                yield return "Collection";
            }
            else if (typeof(IFormFile) != fieldType && typeof(IFormFile).IsAssignableFrom(fieldType))
            {
                yield return "IFormFile";
            }
            yield return "Object";
        }

        public string? FormatValue(object? value, string? format, IFormatProvider formatProvider)
        {
            if (value == null)
            {
                return string.Empty;
            }
            if (string.IsNullOrEmpty(format))
            {
                return Convert.ToString(value, formatProvider);
            }
            return string.Format(formatProvider, format, value);
        }

    }
}
